﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.ComponentModel;

namespace intelliSys.UI.Interaction.MouseGesture
{
    /// <summary>
    /// This gesture is detected using an explicit thread, therefore, it's more accurate and not subject to re-entering threads problem.
    /// <para>This gesture is defined as follow:</para>
    /// <para>*Mouse left button is hold during detection</para>
    /// <para>*Mouse moved toward one direction</para>
    /// <para>*Mouse moved faster in pixel than certian value</para>
    /// <para>*Mouse moved across certain distance</para>
    /// </summary>
    public class Glide : MouseGesture.MouseGestureBase
    {
        public override event EventHandler<MouseGestureEventArgBase> OnSensed;
        /// <summary>
        /// The real-time speed of the mouse, meaning the speed must ALWAYS larger than this to activate the gesture.
        /// </summary>
        public int Speed
        {
            get
            {
                return speed;
            }
            set
            {
                speed = value;
            }
        }
        /// <summary>
        /// How far away should mouse be moved to activate the event
        /// </summary>
        public int Distance
        {
            get
            {
                return distance;
            }
            set
            {
                distance = value;
            }
        }
        /// <summary>
        /// The tolerance value for "Direction" in pixel.
        /// <para>Sometimes, during mouse move, the direction is changed slightly</para>
        /// <para>Therefore, sometimes this gesture is hard to activate</para>
        /// <para>This is the tolerance for direction change.</para>
        /// <para>Set this larger if you feel this gesture is hard to activate.</para>
        /// </summary>
        public int Intensity
        {
            get
            {
                return intensity;
            }
            set
            {
                intensity = value;
            }
        }

        Win32.Point pt;
        Win32.Point startPt;
        Win32.Point currPoint = new Win32.Point(-1, -1);
        object lock_ = new object();
        bool OrientationUp = false;
        bool OrientationRight = false;
        bool currOrientationRight = false;
        bool currOrientationUp = false;
        bool mouseUp = false;
        bool fireInException = true;
        int speed = 0;
        int distance = 50;
        int senseIteration = 0;
        int intensity = 10;
        double distanceCovered = 0;

        public Glide(Window window)
            : base(window)
        {
            SensitivityInterval = 25;
            wnd.PreviewMouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(wnd_PreviewMouseLeftButtonDown);
            wnd.PreviewMouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(wnd_PreviewMouseLeftButtonUp);
        }

        void ClrState()
        {
            fire = false;
            tmr.Enabled = false;
            senseIteration = 0;
            mouseUp = false;
        }

        void wnd_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            System.Threading.Monitor.Enter(lock_);
            mouseUp = true;
            if (fire)
            {
                ClrState();
                if (OnSensed != null)
                {
                    OnSensed(this, new GlideEventArg());
                }
            }
            try
            {
                thread.Abort();
            }
            catch
            {
            }
            mouseUp = false;
            System.Threading.Monitor.Exit(lock_);

        }

        void wnd_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            System.Threading.Monitor.Enter(lock_);
            thread = new System.Threading.Thread(tmr_Elapsed);
            Win32.GetCursorPos(out pt);
            startPt = pt;
            System.Threading.Monitor.Exit(lock_);
            thread.Start();

            // tmr_Elapsed(null, null);
        }

        void tmr_Elapsed(object sender)
        {
            try
            {
                while (true)
                {
                    System.DateTime dt = DateTime.Now;
                    System.Threading.Thread.Sleep(SensitivityInterval);
                    System.Threading.Monitor.Enter(lock_);
                    if (senseIteration == 0)
                    {
                        Win32.GetCursorPos(out currPoint);
                        if (currPoint.X > pt.X)
                        {
                            OrientationRight = true;
                        }
                        else
                        {
                            OrientationRight = false;
                        }
                        if (currPoint.Y > pt.Y)
                        {
                            OrientationUp = true;
                        }
                        else
                        {
                            OrientationUp = false;
                        }
                        senseIteration++;
                        Console.WriteLine("PT(X,Y):({0},{1})", pt.X, pt.Y);
                        Console.WriteLine("CU(X,Y):({0},{1})", currPoint.X, currPoint.Y);
                        pt = currPoint;
                        System.Threading.Monitor.Exit(lock_);
                        continue;
                    }
                    senseIteration++;
                    Win32.GetCursorPos(out currPoint);
                    currOrientationRight = currPoint.X > pt.X;
                    currOrientationUp = currPoint.Y > pt.Y;
                    if ((currOrientationRight != OrientationRight && Math.Abs(currPoint.X - pt.X) >= intensity) || (currOrientationUp != OrientationUp && Math.Abs(currPoint.Y - pt.Y) >= intensity))
                    {
                        //abort
                        Console.WriteLine("changed direction");
                        ClrState();
                        System.Threading.Monitor.Exit(lock_);
                        break;
                    }

                    distanceCovered = Math.Sqrt(Math.Pow(currPoint.X - startPt.X, 2) + Math.Pow(currPoint.Y - startPt.Y, 2));
                    Console.WriteLine("PT(X,Y):({0},{1})", pt.X, pt.Y);
                    Console.WriteLine("CU(X,Y):({0},{1})", currPoint.X, currPoint.Y);
                    var _speed = (distanceCovered / (DateTime.Now - dt).TotalSeconds);
                    Console.WriteLine("speed:" + _speed);
                    if (_speed >= speed)
                    {
                        Console.WriteLine("speed ok:" + _speed + " distance:" + distanceCovered);
                        if (distanceCovered > distance)
                        {
                            Console.WriteLine("distance" + distanceCovered);
                            Console.WriteLine("SHOULD FIRE HERE");
                            if (InstantFire)
                            {
                                ClrState();

                                if (OnSensed != null)
                                {
                                    fireInException = true;
                                    wnd.Dispatcher.BeginInvoke(new Action(() =>
                                        {
                                            Console.WriteLine("FIRED");
                                            OnSensed(this, new GlideEventArg());
                                        }));
                                }
                                System.Threading.Monitor.Exit(lock_);
                                break;
                            }
                            else
                            {
                                fire = true;
                                System.Threading.Monitor.Exit(lock_);
                                break;
                            }
                        }
                        System.Threading.Monitor.Exit(lock_);
                    }
                    else
                    {
                        System.Threading.Monitor.Exit(lock_);
                        ClrState();
                        break;
                    }
                    pt = currPoint;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                try
                {
                    System.Threading.Monitor.Exit(lock_);
                }
                catch (Exception e)
                {
                }
            }
        }
    }
    class GlideEventArg : MouseGestureEventArgBase
    {

    }

}
